﻿using System;
using smp.ButterflyCakes.Models;
using smp.ButterflyCakes.Models.Data;
using smp.ButterflyCakes.Models.FormModels.Order;
using smp.ButterflyCakes.Repository.Databases;
using smp.ButterflyCakes.Repository.Interfaces;
using smp.QueryDb;
using smp.QueryDb.Executing;
using smp.QueryDb.Structure;

namespace smp.ButterflyCakes.Repository.Implementation
{
    public class DomainOrderRepository : IDomainOrderRepository
    {
        private readonly IDbSessionFactory _sessionFactory;
        
        public DomainOrderRepository()
        {
            _sessionFactory = new DbSessionFactory("ButterflyCakes");
        }

        public DomainOrderRepository(IDbSessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
        }

        #region Implementation of IDomainOrderRepository

        public OrderDataItem GetOrderData(Guid id)
        {
            using (var session = _sessionFactory.Create())
            {
                var model = session.Load(new Query<OrderDataItem>()
                                             .From(Db.Order
                                                       .Join(new Join(Db.EntityStage)
                                                                 .On(Order.CurrentLastStageId).IsEqualTo(EntityStage.Id)))
                                             .Map(x => x.Id).To(Order.Id)
                                             .Map(x => x.LastStage).ToNested(new NestedMap<NextOrderStage>()
                                                                                 .Map(x => x.Id).To(EntityStage.Id)
                                                                                 .Map(x => x.Title).To(EntityStage.Title)
                                                                                 .Map(x => x.Stage).To(EntityStage.Stage)
                                                                                 .Map(x => x.Created).To(EntityStage.Created)
                                                                                 .Map(x => x.Index).To(EntityStage.Index)
                                                                                 .Map(x => x.OrderId).To(EntityStage.OrderId))
                                             .Where(new Filter<OrderDataItem>(Order.Id).Is.EqualTo(id)));
                var stages = session.Query(new Query<StageDataItem>()
                                               .From(Db.EntityStage
                                                         .Join(new Join(Db.Component)
                                                                   .On(EntityStage.ComponentId).IsEqualTo(Component.Id)))
                                               .Map(x => x.ComponentId).To(EntityStage.ComponentId)
                                               .Map(x => x.Created).To(EntityStage.Created)
                                               .Map(x => x.Id).To(EntityStage.Id)
                                               .Map(x => x.Index).To(EntityStage.Index)
                                               .Map(x => x.OrderId).To(EntityStage.OrderId)
                                               .Map(x => x.PreviousStageId).To(EntityStage.PreviousStageId)
                                               .Map(x => x.Stage).To(EntityStage.Stage)
                                               .Map(x => x.Title).To(EntityStage.Title)
                                               .OrderBy(EntityStage.Index)
                                               .Where(new Filter<StageDataItem>(EntityStage.OrderId).Is.EqualTo(id)
                                                          .And(Component.Title).Is.EqualTo("Order Details")));
                if(stages != null)
                {
                    model.Stages = stages.Results;
                }
                return model;
            }
        }

        public bool CreateOrder(Guid orderId, Guid detailsId, StageDataItem firstStage)
        {
            using (var session = _sessionFactory.Create())
            {
                session.StartTransaction();
                if (session.Insert(new Insert(Db.Order)
                                          .Set(Order.Id).To(orderId)
                                          .Set(Order.Status).To(Status.Building)
                                          .Set(Order.CurrentLastComponentId).To(detailsId)
                                          .Set(Order.CurrentLastStageId).To(firstStage.Id)
                                          .Set(Order.Created).To(DateTime.Now)) != 1)
                    return false;

                if (session.Insert(new Insert(Db.Component)
                                       .Set(Component.Id).To(detailsId)
                                       .Set(Component.OrderId).To(orderId)
                                       .Set(Component.ComponentType).To(ComponentType.OrderDetails)
                                       .Set(Component.Title).To("Order Details")) != 1)
                    return false;

                if (!CreateStage(firstStage, session))
                    return false;

                session.CommitTransaction();
                return true;
            }
        }

        public bool SaveOrderSummary(OrderSummaryModel model, StageDataItem nextStage)
        {
            using(var session = _sessionFactory.Create())
            {
                session.StartTransaction();
                if(session.Update(new Update(Db.Order)
                                          .Set(Order.Title).To(model.Title)
                                          .Set(Order.Summary).To(model.Summary)
                                          .Set(Order.Due).To(model.Due)
                                          .Set(Order.CurrentLastStageId).To(nextStage.Id)
                                          .Where(new Filter(Order.Id).Is.EqualTo(model.OrderId))) != 1)
                    return false;
                
                if(!CreateStage(nextStage, session))
                    return false;

                session.CommitTransaction();
                return true;
            }
        }

        public bool SaveContactInformation(ContactInformationModel command)
        {
            using (var session = _sessionFactory.Create())
            {
                session.StartTransaction();
                var contactId = command.ContactId;
                if (!command.ContactId.HasValue)
                {
                    contactId = Guid.NewGuid();
                    if (session.Insert(new Insert(Db.Contact)
                                       .Set(Contact.Title).To(command.Title)
                                       .Set(Contact.FirstName).To(command.FirstName)
                                       .Set(Contact.LastName).To(command.LastName)
                                       .Set(Contact.SearchName).To(command.SearchName)
                                       .Set(Contact.Email).To(command.Email)
                                       .Set(Contact.Facebook).To(command.Facebook)
                                       .Set(Contact.PhoneNumber).To(command.PhoneNumber)
                                       .Set(Contact.HouseNameNumber).To(command.HouseNameNumber)
                                       .Set(Contact.Street).To(command.Street)
                                       .Set(Contact.Town).To(command.Town)
                                       .Set(Contact.County).To(command.County)
                                       .Set(Contact.PostCode).To(command.PostCode)
                                       .Set(Contact.Id).To(contactId)) != 1)
                        return false;
                }
                else
                {
                    if (session.Update(new Update(Db.Contact)
                                       .Set(Contact.Title).To(command.Title)
                                       .Set(Contact.FirstName).To(command.FirstName)
                                       .Set(Contact.LastName).To(command.LastName)
                                       .Set(Contact.SearchName).To(command.SearchName)
                                       .Set(Contact.Email).To(command.Email)
                                       .Set(Contact.Facebook).To(command.Facebook)
                                       .Set(Contact.PhoneNumber).To(command.PhoneNumber)
                                       .Set(Contact.HouseNameNumber).To(command.HouseNameNumber)
                                       .Set(Contact.Street).To(command.Street)
                                       .Set(Contact.Town).To(command.Town)
                                       .Set(Contact.County).To(command.County)
                                       .Set(Contact.PostCode).To(command.PostCode)
                                       .Where(new Filter(Contact.Id).Is.EqualTo(command.ContactId))) != 1)
                        return false;
                }

                if (session.Update(new Update(Db.Order)
                                   .Set(Order.ContactId).To(contactId)
                                   .Where(new Filter(Order.Id).Is.EqualTo(command.OrderId))) != 1)
                    return false;

                session.CommitTransaction();
                return true;
            }
        }

        public bool CreateCake(Guid orderId, Guid componentId, StageDataItem firstStage)
        {
            using(var session = _sessionFactory.Create())
            {
                session.StartTransaction();

                if(session.Insert(new Insert(Db.Component)
                    .Set(Component.Id).To(componentId)
                    .Set(Component.OrderId).To(orderId)) != 1)
                    return false;

                if(!CreateStage(firstStage, session))
                    return false;

                if(session.Update(new Update(Db.Order)
                    .Set(Order.CurrentLastStageId).To(firstStage.Id)
                    .Set(Order.CurrentLastComponentId).To(componentId)
                    .Where(new Filter(Order.Id).Is.EqualTo(orderId))) != 1)
                    return false;

                session.CommitTransaction();
                return true;
            }
        }

        #endregion

        private bool CreateStage(StageDataItem stage, IDbSession session)
        {
            if(stage.IsExisting)
                return true;
            return session.Insert(new Insert(Db.EntityStage)
                                      .Set(EntityStage.Id).To(stage.Id)
                                      .Set(EntityStage.Created).To(stage.Created)
                                      .Set(EntityStage.Stage).To(stage.Stage)
                                      .Set(EntityStage.Title).To(stage.Title)
                                      .Set(EntityStage.ComponentId).To(stage.ComponentId)
                                      .Set(EntityStage.OrderId).To(stage.OrderId)
                                      .Set(EntityStage.Index).To(stage.Index)) == 1;
        }
    }
}
